using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.UnitTests.Grammar {
    static class TermAssertionUtilities {
        public static void ShouldBe (this ITerm actualTerm, string termSource) {
            ITerm term = TermParser.Parse (termSource);

            AssertTermEquals (actualTerm, term);
        }

        private static void AssertTermEquals (ITerm actualTerm, ITerm expectedTerm) {
            if (expectedTerm is CompositeTerm) {
                var expectedCompositeTerm = (CompositeTerm) expectedTerm;
                Assert.That (actualTerm, Is.InstanceOf (typeof (CompositeTerm)));
                var actualCompositeTerm = actualTerm as CompositeTerm;
                Assert.That (actualCompositeTerm.Name, Is.EqualTo (expectedCompositeTerm.Name));

                foreach (var subTerm in expectedCompositeTerm.SubTerms) {
                    Assert.That (actualCompositeTerm.SubTerms.ContainsKey (subTerm.Key), "expected `" + actualTerm + "' to contain sub term: `" + subTerm.Key + "'");
                    AssertTermEquals (actualCompositeTerm.SubTerms[subTerm.Key], subTerm.Value);
                }
            } else if (expectedTerm is ListTerm) {
                var expectedListTerm = (ListTerm) expectedTerm;
                Assert.That (actualTerm, Is.InstanceOf (typeof (ListTerm)));

                var actualListTerm = (ListTerm) actualTerm;
                Assert.That (actualListTerm.Terms.Count, Is.EqualTo (expectedListTerm.Terms.Count));

                for (int i = 0; i < expectedListTerm.Terms.Count; i++) {
                    AssertTermEquals (actualListTerm.Terms[i], expectedListTerm.Terms[i]);
                }
            } else if (expectedTerm is IdentifierTerm) {
                Assert.That (((IdentifierTerm) actualTerm).Name, Is.EqualTo (((IdentifierTerm) expectedTerm).Name));
            } else if (expectedTerm is IntegerTerm) {
                Assert.That (((IntegerTerm) actualTerm).Value, Is.EqualTo (((IntegerTerm) expectedTerm).Value));
            } else if (expectedTerm is StringTerm) {
                Assert.That (((StringTerm) actualTerm).Value, Is.EqualTo (((StringTerm) expectedTerm).Value));
            } else {
                Assert.Fail ("term type not expected: " + actualTerm);
            }
        }

        public static bool IsIdentifierTerm (this ITerm term, string name) {
            if (term is IdentifierTerm) {
                return ((IdentifierTerm) term).Name == name;
            } else {
                return false;
            }
        }
    }
}